#include "rpc/test/cli/cli.h"

#include "rpc/socket.h"
#include "rpc/socket_opt.h"
#include "rpc/buffer.h"
#include "rpc/nfs_protocol.h"

DEFINE_string(serv_ip, "172.16.65.100", "rpc test server host");
DEFINE_int32(serv_port, 7789, "rpc test server port");
DEFINE_int32(test_cnt, 1, "send test message count");
DEFINE_int32(test_thread, 1, "test thread count");

#define DEBUG 1

using namespace ufs;
using namespace test;

scoped_ptr<NfsProtocol> protocol(new NfsProtocol);

bool TestCli::Connect(std::string& server, uint16 port) {
  Addr addr(server, port);
  if (!socket_->Connect(addr)) {
    LOG(WARNING) << "connect server error, ip: " << server << " port: " << port;
    return false;
  }

  return true;
}

bool TestCli::SendBuffer(Buffer* buf) {
  size_t all_bytes = buf->ReadableSize();
  ssize_t ret = sock::Send(socket_.get(), buf);
  return ret == (ssize_t) all_bytes;
}

bool TestCli::SendMultiple() {
  Buffer buf1;
  std::string str1(128, 'a');
  uint32 header1 = protocol->EncodeHeader(str1.size(), false);
  LOG(INFO) << "send header1: " << ::ntohl(header1) << " str1: " << str1.size();
  buf1.Append(&header1);
  buf1.Append(&str1);
  if (!SendBuffer(&buf1)) {
    return false;
  }

  Buffer buf2;
  std::string str2(128, 'b');
  uint32 header2 = protocol->EncodeHeader(str2.size(), true);
  LOG(INFO) << "send header2: " << ::ntohl(header2) << "str2: " << str2.size();
  buf2.Append(&header2);
  buf2.Append(&str2);

  return SendBuffer(&buf2);
}

bool TestCli::SendTestMsg() {
#if 0
  bool ret = SendInt32();
  if (!ret) {
    LOG(WARNING) << "send int error";
  }

  ret = SendString();
  if (!ret) {
    LOG(WARNING) << "send string error";
  }

  ret = SendCharArray();
  if (!ret) {
    LOG(WARNING) << "send char array error";
  }

  ret = SendMultiple();
  if (!ret) {
    LOG(WARNING) << "send multiple char array error";
  }
#endif

  return SendAndRecv();
}

bool TestCli::SendInt32() {
  Buffer buf;
  uint32 i = 1;
  uint32 header = protocol->EncodeHeader(sizeof(i), true);

  LOG(INFO) << "send int: " << i;

  buf.Append(&header);
  buf.Append(i);

  return SendBuffer(&buf);
}

bool TestCli::SendCharArray() {
  return false;
}

bool TestCli::SendString() {
  std::string str(128, 'i');
  uint32 header;
  Buffer buf;

  LOG(INFO) << "send string: " << str;

  header = protocol->EncodeHeader(str.size(), true);
  LOG(INFO) << "test client header: " << header;
  buf.Append(&header);
  buf.Append(&str);

  uint32* t_i = (uint32*) buf.Peek();
  CHECK_EQ(header, *t_i) << "header: " << header << " buf header: " << *t_i;

  std::string tmp((const char*) t_i + sizeof(uint32),
                  buf.ReadableSize() - sizeof(uint32));
  CHECK_EQ(tmp, str) << "tmp: " << tmp << " str: " << str;

  return SendBuffer(&buf);
}

bool TestCli::SendAndRecv() {
  if (!SendString()) return false;

  uint32 header = sizeof(uint32);
  int32 readn = sock::Recv(socket_.get(), (u_char*) &header, header);
  CHECK_EQ(readn, sizeof(header));

  uint32 msglen;
  bool is_last;
  protocol->DecodeHeader((const u_char*) &header, sizeof(header), &msglen,
                         &is_last);
  LOG(INFO) << "header: " << ::htonl(header) << " msglen: " << msglen
            << " is last: " << is_last;

  Buffer buf;
  CHECK_EQ(buf.ReadFd(socket_->Fd(), msglen), msglen);
  std::string tmp((char*) buf.Read(msglen), (int32) msglen);
  LOG(INFO) << "buf: " << tmp;
  return true;
}

void testMain() {
  scoped_ptr<TestCli> client(new TestCli);
  if (!client->Connect(FLAGS_serv_ip, FLAGS_serv_port)) {
    return;
  }
  LOG(INFO) << "connect server: " << FLAGS_serv_ip << " port: "
            << FLAGS_serv_port << " successfully!";

  for (int32 i = 0; i < FLAGS_test_cnt; ++i) {
    if (client->SendTestMsg()) {
      LOG(INFO) << "send test message successfully...";
      continue;
    }
    LOG(INFO) << "send test message failed...";
  }
}

void StartTest() {
  std::vector<Thread*> threads;
  for (int32 i = 0; i < FLAGS_test_thread; ++i) {
    Thread* t = new Thread(NewPermanentCallback(testMain));
    t->Start();
    LOG(INFO) << "start thread: " << i;
    threads.push_back(t);
  }

  STLClear(&threads);
}

int main(int argc, char* argv[]) {
  google::InitGoogleLogging(argv[0]);
  google::ParseCommandLineFlags(&argc, &argv, true);

  StartTest();

  return 0;
}
